⭐ DCGAN 을 활용한 CIFAR-10 이미지 생성하기 ⭐

▶ 없던 데이터를 만들어 내는 생성 모델링

✅ 생성 모델링은 지금까지 접해 보았던 기본적인 딥러닝 모델과는 조금 다르다고 하는데, 무엇이 다른 걸까❓❓❓

만약 직접 가위, 바위, 보에 해당하는 사진을 찍어 데이터셋을 만들고, 각 이미지를 알맞는 카테고리로 분류 할 수 있도록 학습했다고 가정해보자.

이러한 모델은 판별 모델링(Discriminative Modeling) 이라고 부른다.

말 그대로 입력받은 데이터를 어떤 기준에 대해 판별하는 것이 목표인 모델링인 것 이다.

반면, 생성 모델링(Generative Modeling) 은 말 그대로 없던 데이터를 생성 하는 것이 목표이다. 가위, 바위, 보에 대입해 본다면 다양한 가위, 바위, 보가 담인 데이터셋에서 각 이미지의 특징을 학습해 그와 비슷한 새로운 사진을 만들어 내야 하는 것이다.

즉, 판별 모델과 생성 모델을 한 마디로 정리하면

판별 모델: 입력된 데이터셋을 특정 기준에 따라 분류하거나, 특정 값을 맞추는 모델
생성 모델: 학습한 데이터셋과 비슷하면서도 기존에는 없던 새로운 데이터셋을 생성하는 모델

▶ CIFAR-10 datasets 을 활용하여 이미지를 생성해보자.

✨ 목차 ✨

STEP 1. 작업환경 구성하기

STEP 2. 데이터셋 구성하기

STEP 3. 생성자 모델 구현하기

STEP 4. 판별자 모델 구현하기

STEP 5. 손실함수와 최적화 함수 구현하기

STEP 6. 훈련과정 상세 기능 구현하기

STEP 7. 학습 과정 진행하기

STEP 8. (optional) GAN 훈련 과정 개선하기

STEP 1. 작업환경 구성하기

필요한 패키지는 신경망 구성에 필요한 Tensorflow, 이미지와 GIF를 다루는 imageio, display, matplotlib, PIL 등 이 필요하다.

STEP 2. 데이터셋 구성하기

필요한 패키지를 실행시켰으니, 데이터셋을 가져와보자.

CIFAR-10 데이터는 tf.keras 안에 있는 datasets 에 이미 들어있어서 꺼내어 사용하면 된다.

load_data() 로 데이터를 로딩할 것인데, 이번에는 분류 문제와 달리, 각 이미지가 어떤 카테고리인지 나타내주는 label이 필요 없다.

즉, CIFAR-10 데이터로 분류 문제를 풀었을 때 필요한 train_y, testy 에 해당하는 데이터는 사용하지 않는다는 것이다. 그래서 코드에서 ' ' (언더스코어) 로 해당 데이터는 무시하도록 한다.

⏳ CIFAR-10 은 32 X 32 픽셀의 이미지, RGB 값으로 3의 값을 가진다.

⏳ 각 픽셀은 0 ~ 255 사이의 정수값을 가진다.

⏳ LeakyReLU 사용을 위해 각 픽셀을 -1, 1 로 정규화 시켜 사용할 것이며 중간값을 0 으로 맞추기 위해 127.5를 뺀 후 127.5 로 나누어줄 것이다.

⏳ CIFAR-10 datasets 에는 5만장의 이미지와 사이즈는 32 X 32 인 것을 확인할 수 있다.

⏳ 다만 한 가지 더 추가되야 할 부분이 있는데, CNN(합성곱) 계층을 다룰 때, 딥러닝에서 이미지를 다루려면 채널 수 에 대한 차원이 필요하다. 컬러의 경우 R, G, B 세 개의 채널이 있고, Gray Scale(흑백) 의 경우 1개의 채널이 필요하다. CIFAR-10 의 경우 RGB 의 3색으로 3개의 채널이 필요하다.

⏳ for 문을 통해 CIFAR-10 dataset 을 잘 들고 온 것을 확인 할 수 있다.

STEP 3. 생성자 모델 구현하기

⏳ 구현해 볼 모델은 DCGAN(Deep ConvolutionalGAN) 이다. DCGAN 은 GAN 의 개념이 처음 소개된 1년 반 이후 발표된 논문으로 이전의 GAN을 발전시켜 고화질의 이미지 생성을 이루어낸 첫 번째 논문으로 평가받고 있다.

이후 발표된 수 많은 GAN 기반 이미 생성 모델들은 대부분 DCGAN 모델을 발전시킨 형태이다.

모델의 구현은 Keras Sequential API 를 활용할 것이다.

▶ Generator Model One

STEP 4. 판별자 모델 구현하기

⏳ 판별자는 가짜 이미지와 진짜 이미지를 입력받으면 각 이미지 별로 '진짜라고 판단되는 정도' 값을 출력한다.
입력은 (32, 32, 3) 크기의 이미지가, 출력은 단 하나의 실수값(진짜라고 판단하는 정도) 이다.

▶ Discriminator Model One

STEP 5. 손실함수와 최적화 함수 구현하기

▶ 손실함수(loss function)


GAN 은 손실함수로 교차 엔트로피(Cross Entropy)를 사용한다. 교차 엔트로피는 두 값이 가까워지기 원하는 것으로 얼마나 큰 차이가 나는지를 정량적으로 계산할 때 많이 쓰인다. 판별자는 한 개의 이미지가 가까인지 진짜인지 나타내는 2개 클래스 간 분류 문제를 풀어야 하므로, 이진 교차 엔트로피(binary corss entropy) 를 사용할 것이다.

각각의 손실함수를 이용해 정량적으로 달성해야하는 결과는 아래와 같을 것이다.

손실함수에 사용할 교차 엔트로피 함수는 tf.keras.losses 라이브러리 안에 있다.

교차 엔트로피를 계산하기 위해 입력할 값은 판별자가 판별한 값인데, 판별자 모델의 마지막 Layer 에는 값을 정규화시키는 sigmoidtanh 함수와 같은 활성화 함수가 없다. 판별자가 출력하는 값은 범위가 정해지지 않아 모든 실수값을 가질 수 있다.

그런데 tf.keras.lossesBinaryCrossEntropy 클래스는 기본적으로 본인에게 들어오는 인풋값이 0 - 1 사이에 분포하는 확률값이라 가정한다(참고). 따라서 from_logitsTrue 로 설정해 줘야 BinaryCrossEntropy 에 입력된 값을 함수 내부에서 sigmoid 함수를 사용해 0 ~ 1 사이의 값으로 정규화 한 후 계산할 수 있다.

cross_entropy 를 활용해 계산할 loss 들은 fake_outputreal_output, 두 가지를 활용한다.

fake_outputreal_outputtf.ones_like()tf.zeros_like() 함수를 이용해서 각각 1 또는 0 을 비교해보자.

⏳ 이 함수들은 특정 벡터와 동일한 크기이면서 값은 1 또는 0 으로 가득 채워진 벡터를 만들고 싶을 때 사용한다.

⤵⤵⤵⤵⤵⤵

▶ generator_loss


generator_lossfake_output 이 1에 가까워지기를 바라므로, tf.ones_like() 와의 교차 엔트로피 값을 계산하면 된다.

⏳ 즉, cross_entropy(tf.ones_like(fake_output), fake_output) 값은
fake_output 이 (Real Image 를 의미하는) 1에 가까울수록 작은 값을 가진다.

⤵⤵⤵⤵⤵⤵

▶ discriminator_loss


discriminator_lossreal_output 값은 1에 가까워지기를,
fake_output 값은 0에 가까워지기를 바라므로, 두 가지 loss값을 모두 계산한다.
real_output 은 1로 채워진 벡터와, fake_output 은 0으로 채워진 벡터와 비교하면 된다.

⏳ 최종 discriminator_loss 값은 이 둘을 더한 값이다.

⤵⤵⤵⤵⤵⤵

▶ discriminator accuracy


⏳ 판별자가 real output, fake output 을 얼마나 정확히 판별하는지의 accuracy 를 계산하는 것도 GAN의 학습에서 중요하다.
특히 두 accuracy 를 따로 계산해서 비교하는 것이 매우 유용할 것이다.

⏳ 만약 판별자가 real output 과 fake output 을 accuracy 가 1.0 에 가까울 정도로 정확하게 판별하는 것이 좋은걸까❓❓❓

⏳ 그렇지 않다. 이 경우 생성자가 만들어내는 fake output 이 real output 과 차이가 많이 나기 때문에 판별자가 매우 쉽게 판별해 내고 있다는 뜻이다.

⏳ 그래서, real accuracy 와 fake accuracy 는 초반에는 1.0 에 가깝게 나오다가, 서서히 낮아져 둘 다 0.5에 가까워지는 것이 이상적이다.

🔥 fake accuracy 가 1.0 에 더 가깝다면 아직은 생성자가 판별자를 잘 속이지 못하고 있다는 뜻이다. 🔥

⤵⤵⤵⤵⤵⤵

▶ 최적화 함수(optimizer)


Adam 최적화 기법 을 활용해서 만들어보자.

⏳ Adam 함수 또한 tf.keras.optimizers 안에 있다. 중요한 하이퍼 파라미터인 "learning rate"는 0.0001로 설정 하고,
학습 품질을 올려보고 싶다면 여러 가지로 값을 바꾸어 가며 학습을 진행해 보자.

⏳ 중요한 점 한 가지는 생성자와 구분자는 따로따로 학습을 진행하는 개별 네트워크이기 때문에
optimizer를 따로 만들어주어야 한다는 점이다.

⤵⤵⤵⤵⤵⤵

⏳ 샘플은 한번에 16장 생성

⏳ 생성할 샘플은 같은 노이즈로 생성해야 그에 대한 진전 과정을 확인할 수 있어서, 고정된 seed 노이즈를 만들어야 한다.

100차원의 노이즈를 총 16개, (16, 100) 형상의 벡터를 만들어보자.

⤵⤵⤵⤵⤵⤵

STEP 6. 훈련과정 상세 기능 구현하기

⏳ train_step 함수를 하나하나 뜯어보자.

  1. 입력 데이터: Real Image 역할을 할 images 한 세트를 입력으로 받음

  2. 생성자 입력 노이즈: generator가 FAKE IMAGE를 생성하기 위한 noise를 images 한 세트와 같은 크기인 BATCH_SIZE 만큼 생성함

  3. tf.GradientTape() 는 가중치 갱신을 위한 Gradient를 자동 미분으로 계산하기 위해 with 구문 열기

  4. generated_images 생성: generator가 noise를 입력받은 후 generated_images 생성

  5. discriminator 판별: discriminator가 Real Image인 images와 Fake Image인.
    generated_images를 각각 입력받은 후 real_output, fake_output 출력

  6. loss 계산: fake_output, real_output으로 generator와 discriminator 각각의 loss 계산

  7. accuracy 계산: fake_output, real_output으로 discriminator의 정확도 계산

  8. gradient 계산: gen_tape와 disc_tape를 활용해 gradient를 자동으로 계산

  9. 모델 학습: 계산된 gradient를 optimizer에 입력해 가중치 갱신

  10. 리턴값: 이번 스텝에 계산된 loss와 accuracy를 리턴

⏳ 한 단계식 학습할 train_step 과 함께 일정 간격으로 학습 현황을 볼 수 있는 함수 설계

⏳ 만들어 놓은 고정된 seed 를 이용해서 결과물을 만들어내므로, 고정된 seed 에 대한 결과물이 개선되는 모습이 확인 가능하다.

STEP 7. 학습 과정 진행하기

지금까지 위에서 한 단계를 학습하는 train_step,

샘플 이미지를 생성하고 저장하기 위한 generate_and_save_images(),

학습과정을 시각화하는 draw_train_history(),

모델까지 저장하기 위한 checkpoint 를 준비하였다.

🔥 이 모든 것을 한곳에 합쳐주자. 🔥

⤵⤵⤵⤵⤵⤵

⏳ 모델을 저장하는 간격은 save_every 와 전체 학습 에폭을 결정하는 EPOCHS 파라미터 설정 후 훈련을 해보자.

⏳ 다음과 같이 train() 함수를 실행시켜 모델이 학습하는 결과물을 실시간으로 확인이 가능하다.

❎ Model one (generator, discriminator) 에 대한 결과를 시각화하였다.

이쁘다.

어쨌든 이미지 생성에 있어서는 실패하였으니 수정이 필요할 것 같다.

image.png

Time for training : 2705 sec

❎ fake accuracy 가 1.0 에 더 가깝다 못해 그냥 1이다.

이 또한 성능 개선을 위해 뭔가를 해야 할 것 같다.

image.png

CPU times: user 27min 1s, sys: 51.2 s, total: 27min 53s Wall time: 45min 7s

STEP 8. (optional) GAN 훈련 과정 개선하기

❗ 개선 모델 Model Two ❗

▶ generator_model_two

▶ Discriminator Model Two

▶ Model Two

image.png

Time for training : 2425 sec

image.png

CPU times: user 40min 45s, sys: 23.8 s, total: 41min 9s Wall time: 40min 26s

❗ 개선 모델 Model Three ❗

▶ Generator Model Three

▶ Discriminator Model Three

image.png

Time for training : 167016 sec

image.png

CPU times: user 13d 13h 42min 19s, sys: 1d 2h 16min 46s, total: 14d 15h 59min 5s Wall time: 1d 22h 23min 37s

❗ 개선 모델 Model Four ❗

▶ Generator Model Four

▶ Discriminator Model Four

image.png

EX01_train_history_0072.png

❗ 마지막 수정 코드가 잘나온 것 같다

루브릭


제목 내용
1. GAN의 두 모델 구조를 통해 이미지를 성공적으로 생성하였다. 오브젝트 종류를 육안으로 구별할 수 있을 만한 이미지를 생성하였다.
2. 생성 이미지 시각화 및 학습 그래프를 통해 GAN 학습이 바르게 진행되었음을 입증하였다. gif를 통해 생성이미지 품질이 서서히 향상되는 것과, fake accuracy가 추세적으로 0.5를 향해 하향하고 있음을 확인하였다.
3. 추가적인 GAN 모델구조 혹은 학습과정 개선 아이디어를 제안하고 이를 적용하였다. 제출 아이디어를 제출 프로젝트에 반영하고, 그 결과가 아이디어 적용 이전보다 향상되었음을 시각적으로 입증하였다.

결과 정리

▶ Model One

model_one_generator.gif

model_one_training.gif

▶ Model Two

model_two_generator.gif

model_two_training.gif

▶ Model Three

CIFAR-100_dcgan.gif

CIFAR-100_loss_accuracy.gif

▶ Model Four

model_four_generator.gif

model_four_training.gif

▶ Model One

⏳ make_generator_model 함수를 만들어 언제든 생성자를 생성가능하게 만들었다.

⏳ 함수 내부에서는 먼저 tf.keras.Sequential() 로 모델을 시작한 후 레이어를 쌓아준다.

⏳ 중요한 레이어는 Conv2DTranspose 레이어다.
Con2DTranspose 층은 일반적인 Conv2D와 반대로 이미지 사이즈를 넓혀주는 층이다. 이 모델에서는 세 번의 Conv2DTranspose 층을 이용해 (8, 8, 128) → (16, 16, 64) → (32, 32, 3) 순으로 이미지를 키워나간다.

⏳ 최종 사이즈는 (32, 32, 3)로 준비했던 데이터셋과 형상이 동일하다.

What is Transposed Convolutional Layer❓❓❓

⏳ 레이어의 사이사이에 특정 층들이 반복되는 것을 볼 수 있다. BatchNormalization 레이어는 신경망의 가중치가 폭발하지 않도록 가중치 값을 정규화 시켜준다.

⏳ 중간층들의 활성화 함수는 모두 LeakyReLU 를 사용하였으며,
마지막 층에는 tanh(하이퍼볼릭탄젠트 함수로 시그모이드 함수를 transformation해서 얻을 수 있다.)를 사용하는데, 이유는 (-1 ~ 1) 이내의 값으로 픽셀 값을 정규화시켰던 데이터셋과 동일하게 하기 위함이다.

🔥 Model One 에 대한 결론은 좋지 못하다

▶ Model Four

✅ Model Four 차이점

⏳ 전체적인 generator model, discriminator model 의 설계

model.add(layers.BatchNormalization(momentum=0.9))
model.add(layers.LeakyReLU(alpha=0.1))

# momentum=0.9 와 alpha=0.1 추가

BatchNormalization 의 momentum

⏳ optimizer

generator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.01, beta_1=0.5)
discriminator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.01, beta_1=0.5)

⏳ BATCH_SIZE

BATCH_SIZE = 16

⏳ EPOCH

EPOCH = 100

⛳ 회고 ⛳